sub_sp = '''
    @SP
    M=M-1
'''

add_sp = '''
    @SP
    M=M+1
'''

operand_symbol_code = {
    'add': 'D+M',
    'sub': 'M-D',
    'neg': '-D',
    'eq': 'JEQ',
    'gt': 'JGT',
    'lt': 'JLT',
    'and': 'M&D',
    'or': 'M|D',
    'not': '!D',
}

PushCmd = {
    "constant":
    '''
    @{}
    D=A
    @SP 
    A=M
    M=D
    '''+add_sp,

    "pointer":
    '''
    @{}
    D=M
    @SP
    A=M
    M=D
    '''+add_sp,

    "static":
    '''
    @{}
    D=M
    @SP
    A=M
    M=D
    '''+add_sp,

    "normal":
    '''
    @{}
    D=A
    @{}
    A=D+M
    D=M
    @SP
    A=M
    M=D
    '''+add_sp,
}

PopCmd = {
    "normal":
    '''
    @{}
    D=A
    @{}
    A=D+M
    D=A
    @R13
    M=D
    '''+sub_sp +
    '''
    @SP
    A=M
    D=M
    @R13
    A=M
    M=D
    ''',

    "pointer":
    sub_sp +
    '''
    @SP
    A=M
    D=M
    @{}
    M=D
    ''',

    "static":
    sub_sp +
    '''
    @SP
    A=M
    D=M
    @{}
    M=D
    '''
}

normal_segment = {
    'local': 'LCL',
    'argument': 'ARG',
    'this': 'THIS',
    'that': 'THAT',
}


def get_new_label(index) -> str:
    return "label" + str(index)


def two_operand(symbol) -> str:
    symbol = operand_symbol_code[symbol]
    return sub_sp + \
        '''
    @SP
    A=M
    D=M
    ''' +\
        sub_sp +\
        f'''
    @SP
    A=M
    M={symbol}
    ''' +\
        add_sp


def one_operand(symbol) -> str:
    symbol = operand_symbol_code[symbol]
    return sub_sp + \
        '''
    @SP
    A=M
    D=M
    ''' +\
        f'''
    @SP
    A=M
    M={symbol}
    ''' +\
        add_sp


def jmp(symbol, label) -> str:
    symbol = operand_symbol_code[symbol]
    return sub_sp + \
        '''
    @SP
    A=M
    D=M
    ''' +\
        sub_sp +\
        f'''
    @SP
    A=M
    D=M-D
    M=-1
    @{label}
    D;{symbol}
    @SP
    A=M
    M=0
({label})
    ''' +\
        add_sp


def push(srcfile, segment, offset) -> str:
    if segment in ['pointer', 'temp']:
        reg = ""
        if segment == 'pointer':
            if offset == '0':
                reg = "THIS"
            elif offset == '1':
                reg = "THAT"
        else:
            # temp 0~7 => R 5~12
            reg = "R"+str(int(offset) + 5)
        return PushCmd['pointer'].format(reg)
    elif segment == 'constant':
        return PushCmd['constant'].format(offset)
    elif segment == 'static':
        return PushCmd['static'].format(srcfile + "." + offset)
    else:
        segment = normal_segment[segment]
        return PushCmd['normal'].format(offset, segment)


def pop(srcfile, segment, offset) -> str:
    if segment in ['pointer', 'temp']:
        reg = ""
        if segment == 'pointer':
            if offset == '0':
                reg = "THIS"
            elif offset == '1':
                reg = "THAT"
        else:
            # temp 0~7 => R 5~12
            reg = "R"+str(int(offset) + 5)
        return PopCmd['pointer'].format(reg)
    elif segment == 'static':
        return PopCmd['static'].format(srcfile + "." + offset)
    else:
        segment = normal_segment[segment]
        return PopCmd['normal'].format(offset, segment)
